from typing import List, Tuple, Optional
from algorithm import Solver, OriginalSolver, SympySolver
from executor import Executor, SingleThreadExecutor, MultiThreadExecutor

class SmartSolver:
    def __init__(self):
        self.solvers = {
            "original": (OriginalSolver, 0),
            "sympy": (SympySolver, 1 if SympySolver.is_available() else -1)
        }
        self.executors = [
            (MultiThreadExecutor, 1 if MultiThreadExecutor.is_available() else -1),
            (SingleThreadExecutor, 0)
        ]
    
    def get_solver(self, method: str = "smart") -> Solver:
        if method == "smart":
            solver_cls, _ = max(self.solvers.values(), key=lambda x: x[1])
        else:
            solver_cls, _ = self.solvers.get(method, (OriginalSolver, 0))
        return solver_cls()
    
    def get_executor(self, multi: Optional[bool] = None, initial_values: List[float] = None) -> Executor:
        if multi is None:  # 智能选择
            return MultiThreadExecutor() if len(initial_values) > 100 else SingleThreadExecutor()
        return MultiThreadExecutor() if multi else SingleThreadExecutor()
    
    def solve(self, expr: str, initial_values: List[float], method: str = "smart", multi: Optional[bool] = None, precision: int = 50) -> Tuple[List[Tuple[Optional[float], Optional[float], Optional[int]]], float]:
        import time
        solver = self.get_solver(method)
        executor = self.get_executor(multi, initial_values)
        
        start_time = time.time()
        results = executor.execute(solver, initial_values, expr, precision=precision)
        elapsed_time = time.time() - start_time
        
        unique_roots = []
        for r, fx, it in sorted(results, key=lambda x: x[0] if x[0] is not None else float('inf')):
            if r is not None and (not unique_roots or abs(r - unique_roots[-1][0]) > 0.01):
                unique_roots.append((r, fx, it))
        
        return unique_roots, elapsed_time